// Decompiled on 周六 2月 22 19:31:49 CST 2025 with Zomboid Decompiler v0.1.3 using Vineflower.
package zombie.chat;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import zombie.Lua.LuaEventManager;
import zombie.characters.Faction;
import zombie.characters.IsoPlayer;
import zombie.chat.defaultChats.AdminChat;
import zombie.chat.defaultChats.FactionChat;
import zombie.chat.defaultChats.GeneralChat;
import zombie.chat.defaultChats.RadioChat;
import zombie.chat.defaultChats.SafehouseChat;
import zombie.chat.defaultChats.SayChat;
import zombie.chat.defaultChats.ServerChat;
import zombie.chat.defaultChats.ShoutChat;
import zombie.chat.defaultChats.WhisperChat;
import zombie.core.Core;
import zombie.core.Translator;
import zombie.core.logger.ExceptionLogger;
import zombie.core.logger.LoggerManager;
import zombie.core.logger.ZLogger;
import zombie.core.network.ByteBufferWriter;
import zombie.core.raknet.UdpConnection;
import zombie.core.raknet.VoiceManagerData;
import zombie.debug.DebugLog;
import zombie.inventory.types.Radio;
import zombie.iso.areas.SafeHouse;
import zombie.network.GameClient;
import zombie.network.PacketTypes;
import zombie.network.chat.ChatType;
import zombie.radio.devices.DeviceData;

public class ChatManager {
    private static ChatManager instance = null;
    private UdpConnection serverConnection = null;
    private final HashMap<Integer, ChatBase> mpChats;
    private final HashMap<String, WhisperChat> whisperChats;
    private final HashMap<String, WhisperChatCreation> whisperChatCreation = new HashMap();
    private final HashMap<Short, ChatTab> tabs;
    private ChatTab focusTab;
    private IsoPlayer player;
    private String myNickname;
    private boolean singlePlayerMode = false;
    private GeneralChat generalChat = null;
    private SayChat sayChat = null;
    private ShoutChat shoutChat = null;
    private FactionChat factionChat = null;
    private SafehouseChat safehouseChat = null;
    private RadioChat radioChat = null;
    private AdminChat adminChat = null;
    private ServerChat serverChat = null;
    private ChatManager.Stage chatManagerStage = ChatManager.Stage.notStarted;
    private static volatile ZLogger logger;
    private static final String logNamePrefix = "client chat";

    private ChatManager() {
        this.mpChats = new HashMap();
        this.tabs = new HashMap();
        this.whisperChats = new HashMap();
    }

    public static ChatManager getInstance() {
        if (instance == null) {
            instance = new ChatManager();
        }

        return instance;
    }

    public boolean isSinglePlayerMode() {
        return this.singlePlayerMode;
    }

    public boolean isWorking() {
        return this.chatManagerStage == ChatManager.Stage.working;
    }

    public void init(boolean isSinglePlayer, IsoPlayer owner) {
        LoggerManager.init();
        LoggerManager.createLogger("client chat " + owner.getDisplayName(), Core.bDebug);
        logger = LoggerManager.getLogger("client chat " + owner.getDisplayName());
        logger.write("Init chat system...", "info");
        logger.write("Mode: " + (isSinglePlayer ? "single player" : "multiplayer"), "info");
        logger.write("Chat owner: " + owner.getDisplayName(), "info");
        this.chatManagerStage = ChatManager.Stage.starting;
        this.singlePlayerMode = isSinglePlayer;
        this.generalChat = null;
        this.sayChat = null;
        this.shoutChat = null;
        this.factionChat = null;
        this.safehouseChat = null;
        this.radioChat = null;
        this.adminChat = null;
        this.serverChat = null;
        this.mpChats.clear();
        this.tabs.clear();
        this.focusTab = null;
        this.whisperChats.clear();
        this.player = owner;
        this.myNickname = this.player.username;
        if (isSinglePlayer) {
            this.serverConnection = null;
            this.sayChat = new SayChat();
            this.sayChat.Init();
            this.generalChat = new GeneralChat();
            this.shoutChat = new ShoutChat();
            this.shoutChat.Init();
            this.radioChat = new RadioChat();
            this.radioChat.Init();
            this.adminChat = new AdminChat();
        } else {
            this.serverConnection = GameClient.connection;
            LuaEventManager.triggerEvent("OnChatWindowInit");
        }
    }

    public void processInitPlayerChatPacket(ByteBuffer bb) {
        this.init(false, IsoPlayer.getInstance());
        short _short = bb.getShort();

        for (int _int = 0; _int < _short; _int++) {
            ChatTab chatTab = new ChatTab(bb.getShort(), zombie.GameWindow.ReadString(bb));
            this.tabs.put(chatTab.getID(), chatTab);
        }

        this.addTab((short)0);
        this.focusOnTab(((ChatTab)this.tabs.get((short)0)).getID());
        LuaEventManager.triggerEvent("OnSetDefaultTab", ((ChatTab)this.tabs.get((short)0)).getTitle());
    }

    public void setFullyConnected() {
        this.chatManagerStage = ChatManager.Stage.working;
    }

    public void processAddTabPacket(ByteBuffer bb) {
        this.addTab(bb.getShort());
    }

    public void processRemoveTabPacket(ByteBuffer bb) {
        this.removeTab(bb.getShort());
    }

    public void processJoinChatPacket(ByteBuffer bb) {
        ChatType chatType = ChatType.valueOf(bb.getInt());
        ChatTab chatTab = (ChatTab)this.tabs.get(bb.getShort());
        Object object = null;
        switch (chatType) {
            case general:
                this.generalChat = new GeneralChat(bb, chatTab, this.player);
                object = this.generalChat;
                break;
            case say:
                this.sayChat = new SayChat(bb, chatTab, this.player);
                this.sayChat.Init();
                object = this.sayChat;
                break;
            case shout:
                this.shoutChat = new ShoutChat(bb, chatTab, this.player);
                this.shoutChat.Init();
                object = this.shoutChat;
                break;
            case whisper:
                WhisperChat whisperChat = new WhisperChat(bb, chatTab, this.player);
                whisperChat.init();
                this.whisperChats.put(whisperChat.getCompanionName(), whisperChat);
                object = whisperChat;
                break;
            case faction:
                this.factionChat = new FactionChat(bb, chatTab, this.player);
                object = this.factionChat;
                break;
            case safehouse:
                this.safehouseChat = new SafehouseChat(bb, chatTab, this.player);
                object = this.safehouseChat;
                break;
            case radio:
                this.radioChat = new RadioChat(bb, chatTab, this.player);
                this.radioChat.Init();
                object = this.radioChat;
                break;
            case admin:
                this.adminChat = new AdminChat(bb, chatTab, this.player);
                object = this.adminChat;
                break;
            case server:
                this.serverChat = new ServerChat(bb, chatTab, this.player);
                object = this.serverChat;
                break;
            default:
                DebugLog.log("Chat of type '" + chatType.toString() + "' is not supported to join to");
                return;
        }

        this.mpChats.put(((ChatBase)object).getID(), object);
        ((ChatBase)object).setFontSize(Core.getInstance().getOptionChatFontSize());
        ((ChatBase)object).setShowTimestamp(Core.getInstance().isOptionShowChatTimestamp());
        ((ChatBase)object).setShowTitle(Core.getInstance().isOptionShowChatTitle());
    }

    public void processLeaveChatPacket(ByteBuffer bb) {
        Integer integer = bb.getInt();
        ChatType chatType = ChatType.valueOf(bb.getInt());
        switch (chatType) {
            case general:
            case say:
            case shout:
            case radio:
            case server:
                DebugLog.log("Chat type is '" + chatType.toString() + "'. Can't leave it. Ignored.");
                break;
            case whisper:
                this.whisperChats.remove(((WhisperChat)this.mpChats.get(integer)).getCompanionName());
                this.mpChats.remove(integer);
                break;
            case faction:
                this.mpChats.remove(integer);
                this.factionChat = null;
                DebugLog.log("You leaved faction chat");
                break;
            case safehouse:
                this.mpChats.remove(integer);
                this.safehouseChat = null;
                DebugLog.log("You leaved safehouse chat");
                break;
            case admin:
                this.mpChats.remove(integer);
                this.removeTab(this.adminChat.getTabID());
                this.adminChat = null;
                DebugLog.log("You leaved admin chat");
                break;
            default:
                DebugLog.log("Chat of type '" + chatType.toString() + "' is not supported to leave to");
        }
    }

    public void processPlayerNotFound(String string) {
        logger.write("Got player not found packet", "info");
        WhisperChatCreation whisperChatCreationx = (WhisperChatCreation)this.whisperChatCreation.get(string);
        if (whisperChatCreationx != null) {
            whisperChatCreationx.status = WhisperChat.ChatStatus.PlayerNotFound;
        }
    }

    public ChatMessage unpackMessage(ByteBuffer bb) {
        int _int = bb.getInt();
        ChatBase chatBase = (ChatBase)this.mpChats.get(_int);
        return chatBase.unpackMessage(bb);
    }

    public void processChatMessagePacket(ByteBuffer bb) {
        ChatMessage chatMessage = this.unpackMessage(bb);
        ChatBase chatBase = chatMessage.getChat();
        if (ChatUtility.chatStreamEnabled(chatBase.getType())) {
            chatBase.showMessage(chatMessage);
            logger.write("Got message from server: " + chatMessage, "info");
        } else {
            DebugLog.log("Can't process message '" + chatMessage.getText() + "' because '" + chatBase.getType() + "' chat is disabled");
            logger.write("Can't process message '" + chatMessage.getText() + "' because '" + chatBase.getType() + "' chat is disabled", "warning");
        }
    }

    public void updateChatSettings(String fontSize, boolean showTimestamp, boolean showTitle) {
        Core.getInstance().setOptionChatFontSize(fontSize);
        Core.getInstance().setOptionShowChatTimestamp(showTimestamp);
        Core.getInstance().setOptionShowChatTitle(showTitle);

        for (ChatBase chatBase : this.mpChats.values()) {
            chatBase.setFontSize(fontSize);
            chatBase.setShowTimestamp(showTimestamp);
            chatBase.setShowTitle(showTitle);
        }
    }

    public void showInfoMessage(String msg) {
        ChatMessage chatMessage = this.sayChat.createInfoMessage(msg);
        this.sayChat.showMessage(chatMessage);
    }

    public void showInfoMessage(String author, String msg) {
        if (this.sayChat != null) {
            ChatMessage chatMessage = this.sayChat.createInfoMessage(msg);
            chatMessage.setAuthor(author);
            this.sayChat.showMessage(chatMessage);
        }
    }

    public void sendMessageToChat(String author, ChatType type, String msg) {
        msg = msg.trim();
        if (!msg.isEmpty()) {
            ChatBase chatBase = this.getChat(type);
            if (chatBase == null) {
                if (Core.bDebug) {
                    throw new IllegalArgumentException("Chat '" + type + "' is null. Chat should be init before use!");
                } else {
                    this.showChatDisabledMessage(type);
                }
            } else {
                ChatMessage chatMessage = chatBase.createMessage(msg);
                chatMessage.setAuthor(author);
                this.sendMessageToChat(chatBase, chatMessage);
            }
        }
    }

    public void sendMessageToChat(ChatType type, String msg) {
        this.sendMessageToChat(this.player.getUsername(), type, msg);
    }

    public synchronized void sendWhisperMessage(String destPlayerName, String msg) {
        logger.write("Send message '" + msg + "' for player '" + destPlayerName + "' in whisper chat", "info");
        if (ChatUtility.chatStreamEnabled(ChatType.whisper)) {
            if (destPlayerName == null || destPlayerName.equalsIgnoreCase(this.myNickname)) {
                logger.write("Message can't be send to yourself");
                this.showServerChatMessage(Translator.getText("UI_chat_whisper_message_to_yourself_error"));
                return;
            }

            if (this.whisperChats.containsKey(destPlayerName)) {
                WhisperChat whisperChat = (WhisperChat)this.whisperChats.get(destPlayerName);
                this.sendMessageToChat(whisperChat, whisperChat.createMessage(msg));
                return;
            }

            if (this.whisperChatCreation.containsKey(destPlayerName)) {
                WhisperChatCreation whisperChatCreation0 = (WhisperChatCreation)this.whisperChatCreation.get(destPlayerName);
                whisperChatCreation0.messages.add(msg);
                return;
            }

            WhisperChatCreation whisperChatCreation1 = this.createWhisperChat(destPlayerName);
            whisperChatCreation1.messages.add(msg);
        } else {
            logger.write("Whisper chat is disabled", "info");
            this.showChatDisabledMessage(ChatType.whisper);
        }
    }

    public Boolean isPlayerCanUseChat(ChatType chat) {
        if (!ChatUtility.chatStreamEnabled(chat)) {
            return false;
        } else {
            switch (chat) {
                case faction:
                    return Faction.isAlreadyInFaction(this.player);
                case safehouse:
                    return SafeHouse.hasSafehouse(this.player) != null;
                case radio:
                    return this.isPlayerCanUseRadioChat();
                case admin:
                    return this.player.isAccessLevel("admin");
                default:
                    return true;
            }
        }
    }

    public void focusOnTab(Short id) {
        for (ChatTab chatTab : this.tabs.values()) {
            if (chatTab.getID() == id) {
                this.focusTab = chatTab;
                return;
            }
        }

        throw new RuntimeException("Tab with id = '" + id + "' not found");
    }

    public String getTabName(short tabID) {
        return this.tabs.containsKey(tabID) ? ((ChatTab)this.tabs.get(tabID)).getTitle() : Short.toString(tabID);
    }

    public ChatTab getFocusTab() {
        return this.focusTab;
    }

    public void showRadioMessage(ChatMessage msg) {
        this.radioChat.showMessage(msg);
    }

    public void showRadioMessage(String text, int channel) {
        ChatMessage chatMessage = this.radioChat.createMessage(text);
        if (channel != 0) {
            chatMessage.setRadioChannel(channel);
        }

        this.radioChat.showMessage(chatMessage);
    }

    public void showStaticRadioSound(String text) {
        this.radioChat.showMessage(this.radioChat.createStaticSoundMessage(text));
    }

    public ChatMessage createRadiostationMessage(String text, int channel) {
        return this.radioChat.createBroadcastingMessage(text, channel);
    }

    public void showServerChatMessage(String msg) {
        ServerChatMessage serverChatMessage = this.serverChat.createServerMessage(msg);
        this.serverChat.showMessage(serverChatMessage);
    }

    private void addMessage(int _int, String string1, String string0) {
        ChatBase chatBase = (ChatBase)this.mpChats.get(_int);
        chatBase.showMessage(string0, string1);
    }

    public void addMessage(String msgAuthor, String msg) throws RuntimeException {
        if (this.generalChat == null) {
            throw new RuntimeException();
        } else {
            this.addMessage(this.generalChat.getID(), msgAuthor, msg);
        }
    }

    private void sendMessageToChat(ChatBase chatBase, ChatMessage chatMessage0) {
        if (chatBase.getType() == ChatType.radio) {
            if (Core.bDebug) {
                throw new IllegalArgumentException("You can't send message to radio directly. Use radio and send say message");
            } else {
                DebugLog.log("You try to use radio chat directly. It's restricted. Try to use say chat");
            }
        } else {
            chatBase.showMessage(chatMessage0);
            if (chatBase.isEnabled()) {
                if (!this.isSinglePlayerMode() && !chatMessage0.isLocal()) {
                    DeviceData deviceData = this.getTransmittingRadio();
                    chatBase.sendToServer(chatMessage0, deviceData);
                    if (deviceData != null && chatBase.isSendingToRadio()) {
                        ChatMessage chatMessage1 = this.radioChat.createMessage(chatMessage0.getText());
                        chatMessage1.setRadioChannel(deviceData.getChannel());
                        this.radioChat.sendToServer(chatMessage1, deviceData);
                    }
                }
            } else {
                this.showChatDisabledMessage(chatBase.getType());
            }
        }
    }

    private ChatBase getChat(ChatType chatType) {
        if (chatType == ChatType.whisper) {
            throw new IllegalArgumentException("Whisper not unique chat");
        } else {
            switch (chatType) {
                case general:
                    return this.generalChat;
                case say:
                    return this.sayChat;
                case shout:
                    return this.shoutChat;
                case whisper:
                default:
                    throw new IllegalArgumentException("Chat type is undefined");
                case faction:
                    return this.factionChat;
                case safehouse:
                    return this.safehouseChat;
                case radio:
                    return this.radioChat;
                case admin:
                    return this.adminChat;
                case server:
                    return this.serverChat;
            }
        }
    }

    private void addTab(short _short) {
        ChatTab chatTab = (ChatTab)this.tabs.get(_short);
        if (!chatTab.isEnabled()) {
            chatTab.setEnabled(true);
            LuaEventManager.triggerEvent("OnTabAdded", chatTab.getTitle(), chatTab.getID());
        }
    }

    private void removeTab(Short _short) {
        ChatTab chatTab = (ChatTab)this.tabs.get(_short);
        if (chatTab.isEnabled()) {
            LuaEventManager.triggerEvent("OnTabRemoved", chatTab.getTitle(), chatTab.getID());
            chatTab.setEnabled(false);
        }
    }

    private WhisperChatCreation createWhisperChat(String string) {
        logger.write("Whisper chat is not created for '" + string + "'", "info");
        WhisperChatCreation whisperChatCreationx = new WhisperChatCreation();
        whisperChatCreationx.destPlayerName = string;
        whisperChatCreationx.status = WhisperChat.ChatStatus.Creating;
        whisperChatCreationx.createTime = System.currentTimeMillis();
        this.whisperChatCreation.put(string, whisperChatCreationx);
        ByteBufferWriter byteBufferWriter = this.serverConnection.startPacket();
        PacketTypes.PacketType.PlayerStartPMChat.doPacket(byteBufferWriter);
        byteBufferWriter.putUTF(this.myNickname);
        byteBufferWriter.putUTF(string);
        PacketTypes.PacketType.PlayerStartPMChat.send(this.serverConnection);
        logger.write("'Start PM chat' package sent. Waiting for a creating whisper chat by server...", "info");
        return whisperChatCreationx;
    }

    public static void UpdateClient() {
        if (instance != null) {
            try {
                instance.updateClient();
            } catch (Throwable throwable) {
                ExceptionLogger.logException(throwable);
            }
        }
    }

    private void updateClient() {
        if (this.isWorking()) {
            this.updateWhisperChat();
        }
    }

    private void updateWhisperChat() {
        if (!this.whisperChatCreation.isEmpty()) {
            long _long = System.currentTimeMillis();

            for (WhisperChatCreation whisperChatCreationx : new ArrayList(this.whisperChatCreation.values())) {
                if (this.whisperChats.containsKey(whisperChatCreationx.destPlayerName)) {
                    WhisperChat whisperChat = (WhisperChat)this.whisperChats.get(whisperChatCreationx.destPlayerName);
                    logger.write(
                        "Whisper chat created between '"
                            + this.myNickname
                            + "' and '"
                            + whisperChatCreationx.destPlayerName
                            + "' and has id = "
                            + whisperChat.getID(),
                        "info"
                    );
                    this.whisperChatCreation.remove(whisperChatCreationx.destPlayerName);

                    for (String string : whisperChatCreationx.messages) {
                        this.sendMessageToChat(whisperChat, whisperChat.createMessage(string));
                    }
                } else if (whisperChatCreationx.status == WhisperChat.ChatStatus.PlayerNotFound) {
                    logger.write("Player '" + whisperChatCreationx.destPlayerName + "' is not found. Chat is not created", "info");
                    this.whisperChatCreation.remove(whisperChatCreationx.destPlayerName);
                    this.showServerChatMessage(Translator.getText("UI_chat_whisper_player_not_found_error", whisperChatCreationx.destPlayerName));
                } else if (whisperChatCreationx.status == WhisperChat.ChatStatus.Creating && _long - whisperChatCreationx.createTime >= 10000L) {
                    logger.write("Whisper chat is not created by timeout. See server chat logs", "error");
                    this.whisperChatCreation.remove(whisperChatCreationx.destPlayerName);
                }
            }
        }
    }

    private void showChatDisabledMessage(ChatType chatType0) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(Translator.getText("UI_chat_chat_disabled_msg", Translator.getText(chatType0.getTitleID())));

        for (ChatType chatType1 : ChatUtility.getAllowedChatStreams()) {
            if (this.isPlayerCanUseChat(chatType1)) {
                stringBuilder.append("    * ").append(Translator.getText(chatType1.getTitleID())).append(" <LINE> ");
            }
        }

        this.showServerChatMessage(stringBuilder.toString());
    }

    private boolean isPlayerCanUseRadioChat() {
        Radio radio = this.player.getEquipedRadio();
        if (radio != null && radio.getDeviceData() != null) {
            boolean _boolean = radio.getDeviceData().getIsTurnedOn();
            _boolean &= radio.getDeviceData().getIsTwoWay();
            _boolean &= radio.getDeviceData().getIsPortable();
            return _boolean & !radio.getDeviceData().getMicIsMuted();
        } else {
            return false;
        }
    }

    private DeviceData getTransmittingRadio() {
        if (this.player.getOnlineID() == -1) {
            return null;
        } else {
            VoiceManagerData voiceManagerData = VoiceManagerData.get(this.player.getOnlineID());
            synchronized (voiceManagerData.radioData) {
                return (DeviceData)voiceManagerData.radioData
                    .stream()
                    .filter(VoiceManagerData.RadioData::isTransmissionAvailable)
                    .findFirst()
                    .map(VoiceManagerData.RadioData::getDeviceData)
                    .orElse(null);
            }
        }
    }

    private static enum Stage {
        notStarted,
        starting,
        working;
    }
}
